﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Microsoft.Ajax.Utilities;
using Nop.Admin.Models.Orders;
using Nop.Core;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Common;
using Nop.Core.Domain.Localization;
using Nop.Core.Domain.Orders;
using Nop.Core.Domain.Payments;
using Nop.Core.Domain.Shipping;
using Nop.Core.Domain.Stores;
using Nop.Services.Catalog;
using Nop.Services.Common;
using Nop.Services.Customers;
using Nop.Services.Helpers;
using Nop.Services.Localization;
using Nop.Services.Logging;
using Nop.Services.Logistics;
using Nop.Services.Messages;
using Nop.Services.Orders;
using Nop.Services.Security;
using Nop.Services.Shipping;
using Nop.Services.Stores;
using Nop.Web.Framework.Controllers;
using Telerik.Web.Mvc;

namespace Nop.Admin.Controllers
{
    [AdminAuthorize]
    public partial class ReturnRequestController : BaseNopController
    {
        #region Fields

        private readonly IOrderService _orderService;
        private readonly IDateTimeHelper _dateTimeHelper;
        private readonly ICustomerService _customerService;
        private readonly ILocalizationService _localizationService;
        private readonly IWorkContext _workContext;
        private readonly IWorkflowMessageService _workflowMessageService;
        private readonly LocalizationSettings _localizationSettings;
        private readonly ICustomerActivityService _customerActivityService;
        private readonly IPermissionService _permissionService;
        private readonly IProductService _productService;
        private readonly ICopyProductService _copyProductService;
        private readonly ILogisticsService _logisticsService;
        private readonly IStoreService _storeService;
        private readonly IAddressService _addressService;
        private readonly Nop.Services.Logistics.IOrderProcessingService _orderProcessingService;
        private readonly IShipmentService _shipmentService;
        private readonly IGenericAttributeService _genericAttributeService;

        private const string ReturnRequestGeneryKeyGroup = "ReturnRequestLogisticOrder";

        #endregion Fields

        #region Constructors

        public ReturnRequestController(IOrderService orderService,
            ICustomerService customerService, IDateTimeHelper dateTimeHelper,
            ILocalizationService localizationService, IWorkContext workContext,
            IWorkflowMessageService workflowMessageService, LocalizationSettings localizationSettings,
            ICustomerActivityService customerActivityService, IPermissionService permissionService,
            IProductService productService, ICopyProductService copyProductService,
            ILogisticsService logisticsService, IStoreService storeService,
            IAddressService addressService, Nop.Services.Logistics.IOrderProcessingService orderProcessingService,
            IShipmentService shipmentService, IGenericAttributeService genericAttributeService)
        {
            this._orderService = orderService;
            this._customerService = customerService;
            this._dateTimeHelper = dateTimeHelper;
            this._localizationService = localizationService;
            this._workContext = workContext;
            this._workflowMessageService = workflowMessageService;
            this._localizationSettings = localizationSettings;
            this._customerActivityService = customerActivityService;
            this._permissionService = permissionService;
            this._productService = productService;
            this._copyProductService = copyProductService;
            this._logisticsService = logisticsService;
            this._storeService = storeService;
            this._addressService = addressService;
            this._orderProcessingService = orderProcessingService;
            this._shipmentService = shipmentService;
            this._genericAttributeService = genericAttributeService;
        }

        #endregion

        #region Utilities

        [NonAction]
        protected bool PrepareReturnRequestModel(ReturnRequestModel model,
            ReturnRequest returnRequest, bool excludeProperties)
        {
            if (model == null)
                throw new ArgumentNullException("model");

            if (returnRequest == null)
                throw new ArgumentNullException("returnRequest");

            var opv = _orderService.GetOrderProductVariantById(returnRequest.OrderProductVariantId);
            if (opv == null)
                return false;

            model.Id = returnRequest.Id;
            model.ProductVariantId = opv.ProductVariantId;
            //product name
            if (!String.IsNullOrEmpty(opv.ProductVariant.Name))
                model.ProductName = string.Format("{0} ({1})", opv.ProductVariant.Product.Name, opv.ProductVariant.Name);
            else
                model.ProductName = opv.ProductVariant.Product.Name;
            model.OrderId = opv.OrderId;
            model.CustomerId = returnRequest.CustomerId;
            model.Quantity = returnRequest.Quantity;
            model.ReturnRequestStatusStr = returnRequest.ReturnRequestStatus.GetLocalizedEnum(_localizationService, _workContext);
            model.CreatedOn = _dateTimeHelper.ConvertToUserTime(returnRequest.CreatedOnUtc, DateTimeKind.Utc);
            if (!excludeProperties)
            {
                model.ReasonForReturn = returnRequest.ReasonForReturn;
                model.RequestedAction = returnRequest.RequestedAction;
                model.CustomerComments = returnRequest.CustomerComments;
                model.StaffNotes = returnRequest.StaffNotes;
                model.ReturnRequestStatusId = returnRequest.ReturnRequestStatusId;
            }
            //model is successfully prepared
            return true;
        }

        #endregion

        #region Methods

        //list
        public ActionResult Index()
        {
            return RedirectToAction("List");
        }

        public ActionResult List()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageReturnRequests))
                return AccessDeniedView();

            var gridModel = new GridModel<ReturnRequestModel>();
            return View(gridModel);
        }

        [HttpPost, GridAction(EnableCustomBinding = true)]
        public ActionResult List(GridCommand command)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageReturnRequests))
                return AccessDeniedView();

            var returnRequests = _orderService.SearchReturnRequests(0, 0, null, command.Page - 1, command.PageSize);
            var returnRequestModels = new List<ReturnRequestModel>();
            foreach (var rr in returnRequests)
            {
                var m = new ReturnRequestModel();
                if (PrepareReturnRequestModel(m, rr, false))
                    returnRequestModels.Add(m);
            }
            var gridModel = new GridModel<ReturnRequestModel>
            {
                Data = returnRequestModels,
                Total = returnRequests.TotalCount,
            };
            return new JsonResult
            {
                Data = gridModel
            };
        }

        //edit
        public ActionResult Edit(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageReturnRequests))
                return AccessDeniedView();
            try
            {
                var returnRequest = _orderService.GetReturnRequestById(id);
                if (returnRequest == null)
                    //No return request found with the specified id
                    return RedirectToAction("List");

                var model = new ReturnRequestModel();
                PrepareReturnRequestModel(model, returnRequest, false);
                //add new open box info
                model.NewName = String.Format(_localizationService.GetResource("Admin.ReturnRequest.NewName"), model.ProductName);
                var opv = _orderService.GetOrderProductVariantById(returnRequest.OrderProductVariantId);
                model.Published = opv.ProductVariant.Published;
                model.NewPrice = opv.ProductVariant.Price;
                return View(model);
            }
            catch (Exception ex)
            {
                ErrorNotification(ex.Message);
                return RedirectToAction("List");
            }
        }

        [HttpPost, ParameterBasedOnFormNameAttribute("save-continue", "continueEditing")]
        [FormValueRequired("save", "save-continue")]
        public ActionResult Edit(ReturnRequestModel model, bool continueEditing)
        {

            if (!_permissionService.Authorize(StandardPermissionProvider.ManageReturnRequests))
                return AccessDeniedView();

            var returnRequest = _orderService.GetReturnRequestById(model.Id);
            if (returnRequest == null)
                //No return request found with the specified id
                return RedirectToAction("List");

            if (ModelState.IsValid)
            {
                returnRequest.ReasonForReturn = model.ReasonForReturn;
                returnRequest.RequestedAction = model.RequestedAction;
                returnRequest.CustomerComments = model.CustomerComments;
                returnRequest.StaffNotes = model.StaffNotes;
                returnRequest.ReturnRequestStatusId = model.ReturnRequestStatusId;
                returnRequest.UpdatedOnUtc = DateTime.UtcNow;

                _customerService.UpdateCustomer(returnRequest.Customer);

                //activity log
                _customerActivityService.InsertActivity("EditReturnRequest", _localizationService.GetResource("ActivityLog.EditReturnRequest"), returnRequest.Id);

                SuccessNotification(_localizationService.GetResource("Admin.ReturnRequests.Updated"));

                //create logistic order if status=ReturnAuthorized
                if (model.ReturnRequestStatusId == (int)ReturnRequestStatus.ReturnAuthorized && IsLogisticOrderExist(model))
                {
                    try
                    {
                        var request = _orderService.GetReturnRequestById(model.Id);
                        CreateLogisticOrder(request);
                    }
                    catch (Exception exception)
                    {
                        ErrorNotification(exception);
                    }
                }

                return continueEditing ? RedirectToAction("Edit", returnRequest.Id) : RedirectToAction("List");
            }

            //If we got this far, something failed, redisplay form
            PrepareReturnRequestModel(model, returnRequest, true);
            return View(model);

        }

        private void CreateLogisticOrder(ReturnRequest model)
        {
            if (model == null) return;
            var orderProductVariant = _orderService.GetOrderProductVariantById(model.OrderProductVariantId);
            var order = _orderService.GetOrderById(orderProductVariant.OrderId);
            if (order == null)
                throw new Exception("order is not exist");
            if (order.StoreId == null)
                throw new Exception("order storeId is not exist");
            //create order
            var shippingAddress = CreateBranchAddressIfNotExist((int)order.StoreId);
            var pickUpAddress = GetPickUpAddress(order);
            var oi = new OrderInformation()
            {
                UserAddressId = pickUpAddress.Id,
                Locations = new List<LocationInformation>(),
                IsCommerce = true,
                OrderKindId = (int)order.OrderKindId
            };
            var li = new LocationInformation()
            {
                PackageNumber = 1,
                IsUserAddressPickup = true,
                AddressId = shippingAddress.Id,
                Packages = new List<PackageInformation>() { new PackageInformation()
                {
                     IsCompactSize = true,
                     OrderKindId = (int)order.OrderKindId
                }}
            };
            oi.Locations.Add(li);

            int orderId = _orderProcessingService.PlaceOrder(oi, _workContext.CurrentCustomer, null, true, true, true);
            if (orderId > 0)
            {
                var newOrder = UpdateNewOrder(orderId, model.Quantity, shippingAddress.Id);

                if (newOrder == null) return;

                _genericAttributeService.InsertAttribute(new GenericAttribute()
                {
                    EntityId = _workContext.CurrentCustomer.Id,
                    Key = model.Id.ToString(),
                    KeyGroup = ReturnRequestGeneryKeyGroup,
                    Value = newOrder.Id.ToString()
                });
            }
        }


        private bool IsLogisticOrderExist(ReturnRequestModel model)
        {
            var genericAttribute = _genericAttributeService.GetAttribute(_workContext.CurrentCustomer.Id, ReturnRequestGeneryKeyGroup,
                  model.Id.ToString());
            if (genericAttribute == null) return true;

            try
            {
                var orderId = int.Parse(genericAttribute.Value);
                var order = _orderService.GetOrderById(orderId);

                return order == null || order.Id <= 0;
            }
            catch (Exception)
            {
                return true;
            }
        }

        private Order UpdateNewOrder(int orderId, int quantity, int shipingAddressId)
        {
            if (orderId <= 0) return null;

            var newOrder = _orderService.GetOrderById(orderId);
            newOrder.PaymentStatusId = (int)PaymentStatus.Paid;
            newOrder.ShippingAddressId = shipingAddressId;

            UpdateOrderProductVariantsQuantityAndOrder(quantity, newOrder);

            _orderService.UpdateOrder(newOrder);

            return newOrder;
        }

        private void UpdateOrderProductVariantsQuantityAndOrder(int quantity, Order order)
        {
            if (order.OrderProductVariants == null || order.OrderProductVariants.Count <= 0 || quantity <= 0) return;

            var varitant = order.OrderProductVariants.FirstOrDefault();

            if (varitant != null && varitant.Id > 0)
            {
                varitant.Quantity = quantity;
            }
        }

        private Address CreateBranchAddressIfNotExist(int storeId)
        {
            var store = _storeService.GetStoreById(storeId);
            if (store == null) throw new Exception("store is not exist~~~");

            var branch = store.Branches.FirstOrDefault();
            if (branch == null || branch.Id <= 0) throw new Exception("branch is not exist");

            Address address;
            if (branch.AddressId == null)
            {
                address = BuildAddressModel(branch);
                _addressService.InsertAddress(address);

                branch.AddressId = address.Id;
                _storeService.UpdateBranch(branch);
            }
            else
            {
                address = _addressService.GetAddressById((int)branch.AddressId);
            }

            return address;
        }

        private static Address BuildAddressModel(Branch branch)
        {
            var address = new Address()
            {
                Address1 = branch.Address1,
                Address2 = branch.Address2,
                CellPhoneNumber = branch.PhoneNumber,
                City = branch.City,
                Company = string.Empty,
                Country = branch.Country,
                CountryId = branch.CountryId,
                CreatedOnUtc = DateTime.UtcNow,
                Email = branch.Email,
                FaxNumber = branch.FaxNumber,
                FirstName = string.Empty,
                LastName = string.Empty,
                PhoneNumber = branch.PhoneNumber,
                StateProvinceId = branch.StateProvinceId,
                TypeId = null,
                VerifiedTypeId = null,
                ZipPostalCode = branch.ZipPostalCode
            };
            return address;
        }

        private Address GetPickUpAddress(Order order)
        {
            var shipment = order.Shipments.FirstOrDefault();

            if (shipment == null || shipment.PackageId <= 0)
                throw new Exception("no shipment is wrong");

            var package = _logisticsService.GetPackageById((int)shipment.PackageId);

            if (package == null)
                throw new Exception("no package is wrong");

            var location = _logisticsService.GetLogisticsLocationById(package.DeliveryLocationId);
            if (location == null)
                throw new Exception("location is null");

            return _addressService.GetAddressById(location.AddressId);
        }

        private IEnumerable<OrderProductVariant> CreateOrderProductVariants(IList<ReturnRequest> returnRequests, Order order)
        {
            var oldOrderProductIds = returnRequests.Select(d => d.OrderProductVariantId).ToList();

            if (oldOrderProductIds == null || oldOrderProductIds.Count <= 0)
                throw new Exception("old order has no orderproductvariants");

            var varitants = new List<ProductVariant>();
            foreach (var item in returnRequests)
            {
                var orderProduct = _orderService.GetOrderProductVariantById(item.OrderProductVariantId);
                if (orderProduct == null) continue;

                var variant = _productService.GetProductVariantById(orderProduct.ProductVariantId);
                if (variant == null) continue;

                varitants.Add(variant);
            }

            if (returnRequests.Count != varitants.Count)
                throw new Exception("return request contains not exist product variant");

            var result = new List<OrderProductVariant>();
            for (int i = 0; i < returnRequests.Count; i++)
            {
                var request = returnRequests[i];
                var varitant = varitants[i];
                result.Add(new OrderProductVariant()
                {
                    OrderProductVariantGuid = Guid.NewGuid(),
                    Order = order,
                    ProductVariantId = varitant.Id,
                    Quantity = request.Quantity,
                    ItemWeight = varitant.Weight,
                    UnitPriceInclTax = decimal.Zero,
                    UnitPriceExclTax = decimal.Zero,
                    PriceInclTax = decimal.Zero,
                    PriceExclTax = 0,
                    DiscountAmountInclTax = decimal.Zero,
                    DiscountAmountExclTax = decimal.Zero,
                    DownloadCount = 0,
                    IsDownloadActivated = false,
                    LicenseDownloadId = 0,
                });
            }

            return result;
        }

        [HttpPost, ActionName("Edit")]
        [FormValueRequired("notify-customer")]
        public ActionResult NotifyCustomer(ReturnRequestModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageReturnRequests))
                return AccessDeniedView();

            var returnRequest = _orderService.GetReturnRequestById(model.Id);
            if (returnRequest == null)
                //No return request found with the specified id
                return RedirectToAction("List");

            //var customer = returnRequest.Customer;
            var opv = _orderService.GetOrderProductVariantById(returnRequest.OrderProductVariantId);
            int queuedEmailId = _workflowMessageService.SendReturnRequestStatusChangedCustomerNotification(returnRequest, opv, _localizationSettings.DefaultAdminLanguageId);
            if (queuedEmailId > 0)
                SuccessNotification(_localizationService.GetResource("Admin.ReturnRequests.Notified"));
            return RedirectToAction("Edit", returnRequest.Id);
        }

        [HttpPost]
        public ActionResult CopyProductVariant(ReturnRequestModel model)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageReturnRequests))
                return AccessDeniedView();
            try
            {
                var returnRequest = _orderService.GetReturnRequestById(model.Id);
                if (returnRequest == null)
                    ModelState.AddModelError("", _localizationService.GetResource("Admin.ReturnRequest.NotExist"));

                if (ModelState.IsValid)
                {
                    int quantity = model.OpenBoxQuantity;
                    if (quantity > returnRequest.Quantity || quantity <= 0)
                    {
                        ErrorNotification(String.Format(_localizationService.GetResource("Admin.ReturnRequests.OpenBox.Quantity.NotMoreThanOrGreaterThan"), returnRequest.Quantity));
                        return RedirectToAction("Edit", new { id = returnRequest.Id });
                    }

                    var opv = _orderService.GetOrderProductVariantById(returnRequest.OrderProductVariantId);
                    if (opv == null)
                    {
                        ErrorNotification(_localizationService.GetResource("Admin.ReturnRequests.NoOPV"));
                        return RedirectToAction("Edit", new { id = returnRequest.Id });
                    }
                    var pv = _productService.GetProductVariantById(opv.ProductVariantId);
                    if (pv == null)
                    {
                        ErrorNotification(_localizationService.GetResource("Admin.ReturnRequests.NoPV"));
                        return RedirectToAction("Edit", new { id = returnRequest.Id });
                    }
                    int regionId = pv.RegionId;
                    if (regionId <= 0)
                    {
                        ErrorNotification(String.Format(_localizationService.GetResource("Admin.ReturnRequests.OpenBox.NoRegion"), pv.Id));
                        return RedirectToAction("Edit", new { id = returnRequest.Id });
                    }

                    int newProductVariantId = _copyProductService.CopyProductVariantAndGetNewId(pv.Id, model.NewName, model.Published, regionId, quantity, model.NewPrice);
                    if (newProductVariantId < 0)
                    {
                        ErrorNotification(_localizationService.GetResource("Admin.ReturnRequests.OpenBox.Add.Failed"));
                    }
                    else if (newProductVariantId == 0)
                    {
                        SuccessNotification(String.Format(_localizationService.GetResource("Admin.ReturnRequests.OpenBox.PVInventory.Add"), quantity));
                    }
                    else
                    {
                        SuccessNotification(_localizationService.GetResource("Admin.ReturnRequests.OpenBox.NewOPV.Create"));
                    }

                    return RedirectToAction("Edit", new { id = returnRequest.Id });
                }
            }
            catch (Exception ex)
            {
                ErrorNotification(ex.Message);
            }

            return RedirectToAction("List");
        }

        //delete
        [HttpPost]
        public ActionResult Delete(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageReturnRequests))
                return AccessDeniedView();

            var returnRequest = _orderService.GetReturnRequestById(id);
            if (returnRequest == null)
                //No return request found with the specified id
                return RedirectToAction("List");

            _orderService.DeleteReturnRequest(returnRequest);

            //activity log
            _customerActivityService.InsertActivity("DeleteReturnRequest", _localizationService.GetResource("ActivityLog.DeleteReturnRequest"), returnRequest.Id);

            SuccessNotification(_localizationService.GetResource("Admin.ReturnRequests.Deleted"));
            return RedirectToAction("List");
        }

        #endregion
    }
}
